Node.js-এ AsyncLocalStorage ব্যবহার করে রিকোয়েস্ট-স্কোপড ভ্যারিয়েবল ম্যানেজমেন্টে দক্ষতা অর্জন করুন। প্রপ ড্রিলিং দূর করে বিশ্বব্যাপী দর্শকদের জন্য আরও পরিচ্ছন্ন এবং পর্যবেক্ষণযোগ্য অ্যাপ্লিকেশন তৈরি করুন।
জাভাস্ক্রিপ্ট অ্যাসিঙ্ক কনটেক্সট উন্মোচন: রিকোয়েস্ট-স্কোপড ভ্যারিয়েবল ম্যানেজমেন্টের উপর একটি গভীর পর্যালোচনা
আধুনিক সার্ভার-সাইড ডেভেলপমেন্টের জগতে, স্টেট ম্যানেজমেন্ট একটি মৌলিক চ্যালেঞ্জ। Node.js নিয়ে কাজ করা ডেভেলপারদের জন্য, এর সিঙ্গল-থ্রেডেড, নন-ব্লকিং, অ্যাসিঙ্ক্রোনাস প্রকৃতির কারণে এই চ্যালেঞ্জ আরও বেড়ে যায়। যদিও এই মডেলটি হাই-পারফরম্যান্স, I/O-বাউন্ড অ্যাপ্লিকেশন তৈরির জন্য অবিশ্বাস্যভাবে শক্তিশালী, এটি একটি অনন্য সমস্যা তৈরি করে: আপনি কীভাবে একটি নির্দিষ্ট রিকোয়েস্টের জন্য কনটেক্সট বজায় রাখবেন যখন এটি বিভিন্ন অ্যাসিঙ্ক্রোনাস অপারেশনের মধ্যে দিয়ে যায়, যেমন মিডলওয়্যার থেকে ডাটাবেস কোয়েরি এবং থার্ড-পার্টি API কল? আপনি কীভাবে নিশ্চিত করবেন যে একজন ব্যবহারকারীর রিকোয়েস্টের ডেটা অন্যজনের রিকোয়েস্টে ফাঁস না হয়?
বহু বছর ধরে, জাভাস্ক্রিপ্ট কমিউনিটি এই সমস্যার সাথে লড়াই করেছে, প্রায়শই "প্রপ ড্রিলিং"-এর মতো কষ্টকর প্যাটার্নের আশ্রয় নিয়েছে—যেখানে ব্যবহারকারীর আইডি বা ট্রেস আইডির মতো রিকোয়েস্ট-নির্দিষ্ট ডেটা একটি কল চেইনের প্রতিটি ফাংশনের মাধ্যমে পাস করা হয়। এই পদ্ধতি কোডকে অগোছালো করে, মডিউলগুলোর মধ্যে টাইট কাপলিং তৈরি করে এবং রক্ষণাবেক্ষণকে একটি দুঃস্বপ্নে পরিণত করে।
এখানেই আসে অ্যাসিঙ্ক কনটেক্সট, একটি ধারণা যা এই দীর্ঘস্থায়ী সমস্যার একটি শক্তিশালী সমাধান প্রদান করে। Node.js-এ স্থিতিশীল AsyncLocalStorage API প্রবর্তনের সাথে, ডেভেলপারদের কাছে এখন রিকোয়েস্ট-স্কোপড ভ্যারিয়েবলগুলো মার্জিত এবং দক্ষতার সাথে পরিচালনা করার জন্য একটি শক্তিশালী, বিল্ট-ইন মেকানিজম রয়েছে। এই নির্দেশিকাটি আপনাকে জাভাস্ক্রিপ্ট অ্যাসিঙ্ক কনটেক্সটের জগতে একটি বিশদ যাত্রায় নিয়ে যাবে, যেখানে সমস্যাটি ব্যাখ্যা করা হবে, সমাধানটি তুলে ধরা হবে এবং বিশ্বব্যাপী ব্যবহারকারীদের জন্য আরও পরিমাপযোগ্য, রক্ষণাবেক্ষণযোগ্য এবং পর্যবেক্ষণযোগ্য অ্যাপ্লিকেশন তৈরি করতে আপনাকে সাহায্য করার জন্য বাস্তবসম্মত উদাহরণ প্রদান করা হবে।
মূল চ্যালেঞ্জ: একটি কনকারেন্ট, অ্যাসিঙ্ক্রোনাস জগতে স্টেট
সমাধানটির সম্পূর্ণ মূল্যায়ন করার জন্য, আমাদের প্রথমে সমস্যার গভীরতা বুঝতে হবে। একটি Node.js সার্ভার হাজার হাজার কনকারেন্ট রিকোয়েস্ট পরিচালনা করে। যখন রিকোয়েস্ট A আসে, Node.js এটি প্রসেস করা শুরু করতে পারে, তারপর একটি ডাটাবেস কোয়েরি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে পারে। যখন এটি অপেক্ষা করছে, তখন এটি রিকোয়েস্ট B তুলে নেয় এবং সেটিতে কাজ শুরু করে। একবার রিকোয়েস্ট A-এর জন্য ডাটাবেসের ফলাফল ফিরে এলে, Node.js তার এক্সিকিউশন আবার শুরু করে। এই অবিরাম কনটেক্সট সুইচিং এর পারফরম্যান্সের পেছনের জাদু, কিন্তু এটি ঐতিহ্যবাহী স্টেট ম্যানেজমেন্ট কৌশলগুলোকে নষ্ট করে দেয়।
গ্লোবাল ভ্যারিয়েবল কেন ব্যর্থ হয়
একজন নবীন ডেভেলপারের প্রথম প্রবৃত্তি হতে পারে একটি গ্লোবাল ভ্যারিয়েবল ব্যবহার করা। উদাহরণস্বরূপ:
let currentUser; // একটি গ্লোবাল ভ্যারিয়েবল
// ব্যবহারকারী সেট করার জন্য মিডলওয়্যার
app.use((req, res, next) => {
currentUser = await getUserFromDb(req.headers.authorization);
next();
});
// অ্যাপ্লিকেশনের গভীরে একটি সার্ভিস ফাংশন
function logActivity() {
console.log(`Activity for user: ${currentUser.id}`);
}
এটি একটি কনকারেন্ট পরিবেশে একটি মারাত্মক ডিজাইন ত্রুটি। যদি রিকোয়েস্ট A currentUser সেট করে এবং তারপর একটি অ্যাসিঙ্ক অপারেশনের জন্য অপেক্ষা করে, তবে রিকোয়েস্ট B এসে রিকোয়েস্ট A শেষ হওয়ার আগেই currentUser ওভাররাইট করে ফেলতে পারে। যখন রিকোয়েস্ট A পুনরায় শুরু হয়, তখন এটি ভুলভাবে রিকোয়েস্ট B-এর ডেটা ব্যবহার করবে। এটি অপ্রত্যাশিত বাগ, ডেটা দুর্নীতি এবং নিরাপত্তা ঝুঁকি তৈরি করে। গ্লোবাল ভ্যারিয়েবল রিকোয়েস্ট-সেফ নয়।
প্রপ ড্রিলিং-এর যন্ত্রণা
আরও সাধারণ এবং নিরাপদ একটি বিকল্প হল "প্রপ ড্রিলিং" বা "প্যারামিটার পাসিং"। এর মধ্যে স্পষ্টভাবে কনটেক্সটটিকে প্রতিটি ফাংশনে একটি আর্গুমেন্ট হিসাবে পাস করা জড়িত যা এটির প্রয়োজন।
ধরা যাক আমাদের অ্যাপ্লিকেশন জুড়ে লগিংয়ের জন্য একটি অনন্য traceId এবং অনুমোদনের জন্য একটি user অবজেক্ট প্রয়োজন।
প্রপ ড্রিলিং-এর উদাহরণ:
// 1. এন্ট্রি পয়েন্ট: মিডলওয়্যার
app.use((req, res, next) => {
const traceId = generateTraceId();
const user = { id: 'user-123', locale: 'en-GB' };
const requestContext = { traceId, user };
processOrder(requestContext, req.body.orderId);
});
// 2. বিজনেস লজিক লেয়ার
function processOrder(context, orderId) {
log('Processing order', context);
const orderDetails = getOrderDetails(context, orderId);
// ... আরও লজিক
}
// 3. ডেটা অ্যাক্সেস লেয়ার
function getOrderDetails(context, orderId) {
log(`Fetching order ${orderId}`, context);
return db.query('SELECT * FROM orders WHERE id = ?', orderId);
}
// 4. ইউটিলিটি লেয়ার
function log(message, context) {
console.log(`[${context.traceId}] [User: ${context.user.id}] - ${message}`);
}
যদিও এটি কাজ করে এবং কনকারেন্সি সমস্যা থেকে নিরাপদ, এর কিছু উল্লেখযোগ্য অসুবিধা রয়েছে:
- কোডের জঞ্জাল:
contextঅবজেক্টটি সর্বত্র পাস করা হয়, এমনকি সেইসব ফাংশনের মাধ্যমেও যারা এটি সরাসরি ব্যবহার করে না কিন্তু তাদের কল করা ফাংশনগুলোতে এটি পাস করতে হয়। - টাইট কাপলিং: প্রতিটি ফাংশন সিগনেচার এখন
contextঅবজেক্টের আকারের সাথে সংযুক্ত। যদি আপনাকে কনটেক্সটে একটি নতুন ডেটা যোগ করতে হয় (যেমন, একটি A/B টেস্টিং ফ্ল্যাগ), তাহলে আপনাকে আপনার কোডবেস জুড়ে কয়েক ডজন ফাংশন সিগনেচার পরিবর্তন করতে হতে পারে। - পাঠযোগ্যতা হ্রাস: একটি ফাংশনের মূল উদ্দেশ্য কনটেক্সট পাস করার বয়লারপ্লেটের কারণে অস্পষ্ট হয়ে যেতে পারে।
- রক্ষণাবেক্ষণের বোঝা: রিফ্যাক্টরিং একটি ক্লান্তিকর এবং ত্রুটিপূর্ণ প্রক্রিয়া হয়ে ওঠে।
আমাদের একটি ভালো উপায় প্রয়োজন ছিল। এমন একটি উপায় যা একটি "জাদুকরী" কন্টেইনারের মতো কাজ করবে, যা রিকোয়েস্ট-নির্দিষ্ট ডেটা ধারণ করবে এবং সেই রিকোয়েস্টের অ্যাসিঙ্ক্রোনাস কল চেইনের যেকোনো জায়গা থেকে অ্যাক্সেস করা যাবে, স্পষ্টভাবে পাস করা ছাড়াই।
`AsyncLocalStorage`-এর আগমন: আধুনিক সমাধান
AsyncLocalStorage ক্লাস, Node.js v13.10.0 থেকে একটি স্থিতিশীল বৈশিষ্ট্য, এই সমস্যার আনুষ্ঠানিক উত্তর। এটি ডেভেলপারদের একটি বিচ্ছিন্ন স্টোরেজ কনটেক্সট তৈরি করতে দেয় যা একটি নির্দিষ্ট এন্ট্রি পয়েন্ট থেকে শুরু হওয়া অ্যাসিঙ্ক্রোনাস অপারেশনগুলোর সম্পূর্ণ চেইন জুড়ে স্থায়ী থাকে।
আপনি এটিকে জাভাস্ক্রিপ্টের অ্যাসিঙ্ক্রোনাস, ইভেন্ট-চালিত বিশ্বের জন্য এক ধরনের "থ্রেড-লোকাল স্টোরেজ" হিসাবে ভাবতে পারেন। যখন আপনি একটি AsyncLocalStorage কনটেক্সটের মধ্যে একটি অপারেশন শুরু করেন, তখন সেই বিন্দু থেকে কল করা যেকোনো ফাংশন—সেটি সিঙ্ক্রোনাস, কলব্যাক-ভিত্তিক বা প্রমিজ-ভিত্তিক হোক—সেই কনটেক্সটে সঞ্চিত ডেটা অ্যাক্সেস করতে পারে।
মূল API কনসেপ্ট
API টি লক্ষণীয়ভাবে সহজ এবং শক্তিশালী। এটি তিনটি মূল পদ্ধতির উপর কেন্দ্র করে:
new AsyncLocalStorage(): স্টোরের একটি নতুন ইনস্ট্যান্স তৈরি করে। আপনি সাধারণত প্রতিটি ধরণের কনটেক্সটের জন্য একটি ইনস্ট্যান্স তৈরি করেন (যেমন, সমস্ত HTTP রিকোয়েস্টের জন্য একটি) এবং এটি আপনার অ্যাপ্লিকেশন জুড়ে শেয়ার করেন।als.run(store, callback): এটি প্রধান কার্যকরী অংশ। এটি একটি ফাংশন (callback) চালায় এবং একটি নতুন অ্যাসিঙ্ক্রোনাস কনটেক্সট স্থাপন করে। প্রথম আর্গুমেন্ট,store, হল সেই ডেটা যা আপনি সেই কনটেক্সটের মধ্যে উপলব্ধ করতে চান।callback-এর ভিতরে সম্পাদিত যেকোনো কোড, অ্যাসিঙ্ক অপারেশন সহ, এইstore-এ অ্যাক্সেস পাবে।als.getStore(): এই পদ্ধতিটি বর্তমান কনটেক্সট থেকে ডেটা (store) পুনরুদ্ধার করতে ব্যবহৃত হয়। যদিrun()দ্বারা প্রতিষ্ঠিত কনটেক্সটের বাইরে কল করা হয়, তবে এটিundefinedফেরত দেবে।
ব্যবহারিক প্রয়োগ: একটি ধাপে ধাপে নির্দেশিকা
চলুন আমাদের আগের প্রপ-ড্রিলিং উদাহরণটি AsyncLocalStorage ব্যবহার করে রিফ্যাক্টর করি। আমরা একটি স্ট্যান্ডার্ড Express.js সার্ভার ব্যবহার করব, কিন্তু নীতিটি যেকোনো Node.js ফ্রেমওয়ার্ক বা এমনকি নেটিভ http মডিউলের জন্যও একই।
ধাপ ১: একটি কেন্দ্রীয় `AsyncLocalStorage` ইনস্ট্যান্স তৈরি করুন
এটি একটি সেরা অনুশীলন হল আপনার স্টোরের একটি একক, শেয়ার্ড ইনস্ট্যান্স তৈরি করা এবং এটিকে এক্সপোর্ট করা যাতে এটি আপনার অ্যাপ্লিকেশন জুড়ে ব্যবহার করা যায়। চলুন asyncContext.js নামে একটি ফাইল তৈরি করি।
// asyncContext.js
import { AsyncLocalStorage } from 'async_hooks';
export const requestContextStore = new AsyncLocalStorage();
ধাপ ২: একটি মিডলওয়্যার দিয়ে কনটেক্সট স্থাপন করুন
কনটেক্সট শুরু করার আদর্শ জায়গা হল একটি রিকোয়েস্টের জীবনচক্রের একেবারে শুরুতে। একটি মিডলওয়্যার এর জন্য উপযুক্ত। আমরা আমাদের রিকোয়েস্ট-নির্দিষ্ট ডেটা তৈরি করব এবং তারপর বাকি রিকোয়েস্ট হ্যান্ডলিং লজিককে als.run()-এর ভিতরে র্যাপ করব।
// server.js
import express from 'express';
import { requestContextStore } from './asyncContext.js';
import { v4 as uuidv4 } from 'uuid'; // একটি অনন্য traceId তৈরির জন্য
const app = express();
// জাদুকরী মিডলওয়্যার
app.use((req, res, next) => {
const traceId = req.headers['x-request-id'] || uuidv4();
const user = { id: 'user-123', locale: 'en-GB' }; // একটি আসল অ্যাপে, এটি একটি প্রমাণীকরণ মিডলওয়্যার থেকে আসে
const store = { traceId, user };
// এই রিকোয়েস্টের জন্য কনটেক্সট স্থাপন করুন
requestContextStore.run(store, () => {
next();
});
});
// ... আপনার রুট এবং অন্যান্য মিডলওয়্যার এখানে যাবে
এই মিডলওয়্যারে, প্রতিটি ইনকামিং রিকোয়েস্টের জন্য, আমরা traceId এবং user সম্বলিত একটি store অবজেক্ট তৈরি করি। তারপর আমরা requestContextStore.run(store, ...) কল করি। ভিতরের next() কলটি নিশ্চিত করে যে এই নির্দিষ্ট রিকোয়েস্টের জন্য পরবর্তী সমস্ত মিডলওয়্যার এবং রুট হ্যান্ডলার এই নতুন তৈরি কনটেক্সটের মধ্যে কার্যকর হবে।
ধাপ ৩: কোনো প্রপ ড্রিলিং ছাড়াই যেকোনো জায়গায় কনটেক্সট অ্যাক্সেস করুন
এখন, আমাদের অন্যান্য মডিউলগুলো আমূল সরল করা যেতে পারে। তাদের আর একটি context প্যারামিটারের প্রয়োজন নেই। তারা কেবল আমাদের requestContextStore ইম্পোর্ট করে getStore() কল করতে পারে।
রিফ্যাক্টর করা লগিং ইউটিলিটি:
// logger.js
import { requestContextStore } from './asyncContext.js';
export function log(message) {
const context = requestContextStore.getStore();
if (context) {
const { traceId, user } = context;
console.log(`[${traceId}] [User: ${user.id}] - ${message}`);
} else {
// একটি রিকোয়েস্ট কনটেক্সটের বাইরের লগের জন্য ফলব্যাক
console.log(`[NO_CONTEXT] - ${message}`);
}
}
রিফ্যাক্টর করা বিজনেস এবং ডেটা লেয়ার:
// orderService.js
import { log } from './logger.js';
import * as db from './database.js';
export function processOrder(orderId) {
log('Processing order'); // কোনো কনটেক্সটের প্রয়োজন নেই!
const orderDetails = getOrderDetails(orderId);
// ... আরও লজিক
}
function getOrderDetails(orderId) {
log(`Fetching order ${orderId}`); // লগার স্বয়ংক্রিয়ভাবে কনটেক্সট তুলে নেবে
return db.query('SELECT * FROM orders WHERE id = ?', orderId);
}
পার্থক্যটি দিন ও রাতের মতো। কোডটি নাটকীয়ভাবে পরিষ্কার, আরও পাঠযোগ্য এবং কনটেক্সটের গঠন থেকে সম্পূর্ণ বিচ্ছিন্ন। আমাদের লগিং ইউটিলিটি, বিজনেস লজিক এবং ডেটা অ্যাক্সেস লেয়ারগুলো এখন বিশুদ্ধ এবং তাদের নির্দিষ্ট কাজে মনোনিবেশ করেছে। যদি আমাদের কখনও রিকোয়েস্ট কনটেক্সটে একটি নতুন প্রপার্টি যোগ করার প্রয়োজন হয়, তবে আমাদের কেবল সেই মিডলওয়্যারটি পরিবর্তন করতে হবে যেখানে এটি তৈরি হয়েছে। অন্য কোনো ফাংশন সিগনেচার স্পর্শ করার প্রয়োজন নেই।
উন্নত ব্যবহার এবং একটি বিশ্বব্যাপী প্রেক্ষিত
রিকোয়েস্ট-স্কোপড কনটেক্সট শুধু লগিংয়ের জন্য নয়। এটি অত্যাধুনিক, বিশ্বব্যাপী অ্যাপ্লিকেশন তৈরির জন্য প্রয়োজনীয় বিভিন্ন শক্তিশালী প্যাটার্ন আনলক করে।
১. ডিস্ট্রিবিউটেড ট্রেসিং এবং পর্যবেক্ষণযোগ্যতা
একটি মাইক্রোসার্ভিসেস আর্কিটেকচারে, একটি ব্যবহারকারীর একক অ্যাকশন একাধিক পরিষেবা জুড়ে রিকোয়েস্টের একটি চেইন ট্রিগার করতে পারে। সমস্যা ডিবাগ করার জন্য, আপনাকে এই পুরো যাত্রাটি ট্রেস করতে সক্ষম হতে হবে। AsyncLocalStorage হল আধুনিক ট্রেসিংয়ের ভিত্তি। আপনার এপিআই গেটওয়েতে একটি ইনকামিং রিকোয়েস্টকে একটি অনন্য traceId বরাদ্দ করা যেতে পারে। এই আইডিটি তখন অ্যাসিঙ্ক কনটেক্সটে সংরক্ষণ করা হয় এবং স্বয়ংক্রিয়ভাবে যেকোনো আউটবাউন্ড এপিআই কলে (যেমন, একটি HTTP হেডার হিসাবে) ডাউনস্ট্রিম পরিষেবাগুলিতে অন্তর্ভুক্ত করা হয়। প্রতিটি পরিষেবা একই কাজ করে, কনটেক্সট প্রচার করে। কেন্দ্রীভূত লগিং প্ল্যাটফর্মগুলো তখন এই লগগুলো গ্রহণ করতে পারে এবং আপনার পুরো সিস্টেম জুড়ে একটি রিকোয়েস্টের সম্পূর্ণ, এন্ড-টু-এন্ড ফ্লো পুনর্গঠন করতে পারে।
২. আন্তর্জাতিকীকরণ (i18n) এবং স্থানীয়করণ (l10n)
একটি বিশ্বব্যাপী অ্যাপ্লিকেশনের জন্য, ব্যবহারকারীর স্থানীয় বিন্যাসে তারিখ, সময়, সংখ্যা এবং মুদ্রা উপস্থাপন করা অত্যন্ত গুরুত্বপূর্ণ। আপনি ব্যবহারকারীর রিকোয়েস্ট হেডার বা ব্যবহারকারী প্রোফাইল থেকে তার লোকেল (যেমন, 'fr-FR', 'ja-JP', 'en-US') অ্যাসিঙ্ক কনটেক্সটে সংরক্ষণ করতে পারেন।
// মুদ্রা বিন্যাসের জন্য একটি ইউটিলিটি
import { requestContextStore } from './asyncContext.js';
function formatCurrency(amount, currencyCode) {
const context = requestContextStore.getStore();
const locale = context?.user?.locale || 'en-US'; // একটি ডিফল্টে ফলব্যাক
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode,
}).format(amount);
}
// অ্যাপের গভীরে ব্যবহার
const priceString = formatCurrency(199.99, 'EUR'); // স্বয়ংক্রিয়ভাবে ব্যবহারকারীর লোকেল ব্যবহার করে
এটি locale ভ্যারিয়েবলটি সর্বত্র পাস না করেই একটি সামঞ্জস্যপূর্ণ ব্যবহারকারীর অভিজ্ঞতা নিশ্চিত করে।
৩. ডেটাবেস লেনদেন ব্যবস্থাপনা
যখন একটি একক রিকোয়েস্টকে একাধিক ডেটাবেস রাইট সম্পাদন করতে হয় যা অবশ্যই একসাথে সফল বা ব্যর্থ হতে হবে, তখন আপনার একটি লেনদেনের প্রয়োজন। আপনি একটি রিকোয়েস্ট হ্যান্ডলারের শুরুতে একটি লেনদেন শুরু করতে পারেন, লেনদেন ক্লায়েন্টকে অ্যাসিঙ্ক কনটেক্সটে সংরক্ষণ করতে পারেন এবং তারপরে সেই রিকোয়েস্টের মধ্যে পরবর্তী সমস্ত ডেটাবেস কল স্বয়ংক্রিয়ভাবে একই লেনদেন ক্লায়েন্ট ব্যবহার করবে। হ্যান্ডলারের শেষে, আপনি ফলাফলের উপর ভিত্তি করে লেনদেনটি কমিট বা রোল ব্যাক করতে পারেন।
৪. ফিচার টগলিং এবং A/B টেস্টিং
আপনি একটি রিকোয়েস্টের শুরুতে নির্ধারণ করতে পারেন যে একজন ব্যবহারকারী কোন ফিচার ফ্ল্যাগ বা A/B টেস্ট গ্রুপের অন্তর্গত এবং এই তথ্যটি কনটেক্সটে সংরক্ষণ করতে পারেন। আপনার অ্যাপ্লিকেশনের বিভিন্ন অংশ, এপিআই লেয়ার থেকে রেন্ডারিং লেয়ার পর্যন্ত, তখন কনটেক্সটের সাথে পরামর্শ করে সিদ্ধান্ত নিতে পারে কোন ফিচারের কোন সংস্করণটি কার্যকর করতে হবে বা কোন UI প্রদর্শন করতে হবে, যা জটিল প্যারামিটার পাসিং ছাড়াই একটি ব্যক্তিগতকৃত অভিজ্ঞতা তৈরি করে।
পারফরম্যান্স বিবেচনা এবং সেরা অনুশীলন
একটি সাধারণ প্রশ্ন হল: পারফরম্যান্স ওভারহেড কী? Node.js কোর টিম AsyncLocalStorage কে অত্যন্ত দক্ষ করার জন্য উল্লেখযোগ্য প্রচেষ্টা বিনিয়োগ করেছে। এটি C++-স্তরের async_hooks API-এর উপর নির্মিত এবং V8 জাভাস্ক্রিপ্ট ইঞ্জিনের সাথে গভীরভাবে সংহত। বেশিরভাগ ওয়েব অ্যাপ্লিকেশনের জন্য, পারফরম্যান্সের প্রভাব নগণ্য এবং কোডের গুণমান এবং রক্ষণাবেক্ষণযোগ্যতায় ব্যাপক উন্নতির দ্বারা এটি ছাপিয়ে যায়।
এটি কার্যকরভাবে ব্যবহার করার জন্য, এই সেরা অনুশীলনগুলো অনুসরণ করুন:
- একটি সিঙ্গলটন ইনস্ট্যান্স ব্যবহার করুন: আমাদের উদাহরণে যেমন দেখানো হয়েছে, সামঞ্জস্যতা নিশ্চিত করতে আপনার রিকোয়েস্ট কনটেক্সটের জন্য
AsyncLocalStorage-এর একটি একক, এক্সপোর্ট করা ইনস্ট্যান্স তৈরি করুন। - এন্ট্রি পয়েন্টে কনটেক্সট স্থাপন করুন:
als.run()কল করার জন্য সর্বদা একটি টপ-লেভেল মিডলওয়্যার বা একটি রিকোয়েস্ট হ্যান্ডলারের শুরু ব্যবহার করুন। এটি আপনার কনটেক্সটের জন্য একটি পরিষ্কার এবং অনুমানযোগ্য সীমানা তৈরি করে। - স্টোরকে অপরিবর্তনীয় হিসাবে বিবেচনা করুন: যদিও স্টোর অবজেক্টটি নিজে পরিবর্তনযোগ্য, তবে এটিকে অপরিবর্তনীয় হিসাবে বিবেচনা করা একটি ভালো অভ্যাস। যদি আপনাকে রিকোয়েস্টের মাঝে ডেটা যোগ করতে হয়, তবে অন্য একটি
run()কল দিয়ে একটি নেস্টেড কনটেক্সট তৈরি করা প্রায়শই পরিচ্ছন্ন, যদিও এটি একটি আরও উন্নত প্যাটার্ন। - কনটেক্সট ছাড়া কেসগুলো পরিচালনা করুন: আমাদের লগারের মতো, আপনার ইউটিলিটিগুলোর সর্বদা পরীক্ষা করা উচিত যে
getStore()undefinedফেরত দেয় কিনা। এটি তাদের একটি রিকোয়েস্ট কনটেক্সটের বাইরে, যেমন ব্যাকগ্রাউন্ড স্ক্রিপ্টে বা অ্যাপ্লিকেশন স্টার্টআপের সময়, সুন্দরভাবে কাজ করতে দেয়। - ত্রুটি হ্যান্ডলিং ঠিকঠাক কাজ করে: অ্যাসিঙ্ক কনটেক্সট সঠিকভাবে
Promiseচেইন,.then()/.catch()/.finally()ব্লক, এবংtry/catchসহasync/awaitএর মাধ্যমে প্রচারিত হয়। আপনাকে বিশেষ কিছু করার দরকার নেই; যদি একটি ত্রুটি নিক্ষেপ করা হয়, কনটেক্সট আপনার ত্রুটি হ্যান্ডলিং লজিকে উপলব্ধ থাকে।
উপসংহার: নোড.জেএস অ্যাপ্লিকেশনের জন্য একটি নতুন যুগ
AsyncLocalStorage কেবল একটি সুবিধাজনক ইউটিলিটি নয়; এটি সার্ভার-সাইড জাভাস্ক্রিপ্টে স্টেট ম্যানেজমেন্টের জন্য একটি দৃষ্টান্ত পরিবর্তনকে প্রতিনিধিত্ব করে। এটি একটি অত্যন্ত কনকারেন্ট পরিবেশে রিকোয়েস্ট-স্কোপড কনটেক্সট পরিচালনার দীর্ঘস্থায়ী সমস্যার একটি পরিষ্কার, শক্তিশালী এবং পারফরম্যান্ট সমাধান প্রদান করে।
এই API গ্রহণ করে, আপনি করতে পারেন:
- প্রপ ড্রিলিং দূর করুন: আরও পরিষ্কার, আরও মনোনিবেশিত ফাংশন লিখুন।
- আপনার মডিউলগুলোকে ডিকাপল করুন: নির্ভরতা হ্রাস করুন এবং আপনার কোডকে রিফ্যাক্টর এবং পরীক্ষা করা সহজ করুন।
- পর্যবেক্ষণযোগ্যতা বৃদ্ধি করুন: শক্তিশালী ডিস্ট্রিবিউটেড ট্রেসিং এবং কনটেক্সচুয়াল লগিং সহজে বাস্তবায়ন করুন।
- অত্যাধুনিক বৈশিষ্ট্য তৈরি করুন: লেনদেন ব্যবস্থাপনা এবং আন্তর্জাতিকীকরণের মতো জটিল প্যাটার্নগুলো সহজ করুন।
Node.js-এ আধুনিক, পরিমাপযোগ্য এবং বিশ্বব্যাপী সচেতন অ্যাপ্লিকেশন তৈরি করা ডেভেলপারদের জন্য, অ্যাসিঙ্ক কনটেক্সটে দক্ষতা অর্জন করা আর ঐচ্ছিক নয়—এটি একটি অপরিহার্য দক্ষতা। পুরানো প্যাটার্নগুলো থেকে সরে এসে এবং AsyncLocalStorage গ্রহণ করে, আপনি এমন কোড লিখতে পারেন যা কেবল আরও দক্ষই নয়, বরং গভীরভাবে আরও মার্জিত এবং রক্ষণাবেক্ষণযোগ্য।